package x86;

import assem.Instr;
import assem.OPER;
import util.List;

import temp.Label;
import temp.Temp;
import tree.Exp;
import tree.Stm;
import tree.BINOP;
import tree.CALL;
import tree.CJUMP;
import tree.CONST;
import tree.ESEQ;
import tree.EXPSTM;
import tree.JUMP;
import tree.LABEL;
import tree.MEM;
import tree.MOVE;
import tree.NAME;
import tree.SEQ;
import tree.TEMP;


public class Codegen
{
    Frame frame;
    public Codegen(Frame f)
    {
        frame=f;        
    }

    private List<Instr> ilist=null;
    private List<Instr> last=null;

    private void emit(Instr inst)
    {
        if (last!=null)
            last = last.tail = new List<Instr>(inst,null);
        else 
            last = ilist = new List<Instr>(inst,null);
    }

    /**
     * Usando Maximal Munch vai percorrendo a arvore buscando 
     * capturar o maior numero de nohs para gerar uma ou um conjunto
     * de instrucoes que geram aquele pedaco da arvore 
     * @param s inicio da arvore
     */
    private void munchStm (Stm s) 
    {
    	/* possiveis extensoes de Stm
		 * CJUMP, EXPSTM, JUMP, LABEL, MOVE, SEQ 
		 * Verifica qual a correta para chamar o 
		 * metodo correto para tratar
		 */
		if (s instanceof CJUMP)
		{
			munchCJump((CJUMP) s);
		}
		else if (s instanceof EXPSTM)
		{
			munchExpStm((EXPSTM) s);
		}
		else if (s instanceof JUMP)
		{
			munchJump((JUMP) s);
		}
		else if (s instanceof LABEL)
		{
			munchLabel((LABEL) s);
		}
		else if (s instanceof MOVE)
		{
			munchMove((MOVE) s);
		}
		else if (s instanceof SEQ)
		{
			munchSeq((SEQ) s);
		}
		else
		{
			System.out.println("ERRO: Statement nao eh instanceof ninguem em munchStm (Stm s)");
		}
    }

	/**
     * Trata uma operacao Binaria
     * Verifica o tipo da operacao e emite
     * uma instrucao de acordo
     */
    private Temp munchBinOp(BINOP s) 
    {
    	/* Objeto a ser retornado*/
    	Temp temp_ret = new Temp();
    	
    	/* Avalia o lado esquerdo da operacao */
    	Temp lado_esq = munchExp(s.left);
    	
    	/* Avalia o lado direito em caso de SHIFT nao precisa */
    	Temp lado_dir = null;
    	if(BINOP.ARSHIFT != s.binop && 
    	   BINOP.LSHIFT  != s.binop &&
    	   BINOP.RSHIFT  != s.binop)
    	{
    		lado_dir = munchExp(s.right);
    	}
  
    	/**
    	 * verifica que operacao eh para
    	 * emitir a instrucao correta
    	 */
    	switch(s.binop)
    	{
    	case BINOP.PLUS:
    	
    		/* atualiza o valor da esquerda e faz a soma */ 
			emit(new OPER("MOV `d0, `s0 ;munchBinOp (PLUS)", new List<Temp>(temp_ret, null), new List<Temp>(lado_esq, null)));
			emit(new OPER("ADD `d0, `s0 ;munchBinOp (PLUS)", new List<Temp>(temp_ret, null), new List<Temp>(lado_dir, new List<Temp>(temp_ret, null))));
			break;    		
        
    	case BINOP.MINUS:
    	
    		/* atualiza o valor da esquerda e faz a subtracao */ 
			emit(new OPER("MOV `d0, `s0 ;munchBinOp (MINUS)", new List<Temp>(temp_ret, null), new List<Temp>(lado_esq, null)));
			emit(new OPER("SUB `d0, `s0 ;munchBinOp (MINUS)", new List<Temp>(temp_ret, null), new List<Temp>(lado_dir, new List<Temp>(temp_ret, null))));
			break;  
        
    	case BINOP.TIMES:
        
        	/* atualiza o valor da esquerda em EAX (EAX = operacoes com inteiros "grandes" */ 
			emit(new OPER("MOV `d0, `s0 ;munchBinOp (TIMES 0)", new List<Temp>(frame.eax, null), new List<Temp>(lado_esq, null)));
			
			/* faz a multiplicacao, salva o resultado */
			emit(new OPER("IMUL `d0, `s0 ;munchBinOp (TIMES 1)", new List<Temp>(frame.eax, null), new List<Temp>(lado_dir, new List<Temp>(frame.eax, null))));
			emit(new OPER("MOV `d0, `s0 ;munchBinOp (TIMES 3)", new List<Temp>(temp_ret, null), new List<Temp>(frame.eax, null)));
			break;
        
    	case BINOP.DIV:
        
    		/* atualiza o valor da esquerda em EAX (EAX = operacoes com inteiros "grandes" */ 
			emit(new OPER("MOV `d0, `s0 ;munchBinOp (DIV 0)", new List<Temp>(frame.eax, null), new List<Temp>(lado_esq, null)));
			
			/* faz a divisao, salva o resultado */
			emit(new OPER("IDIV `d0, `s0 ;munchBinOp (DIV 1)", new List<Temp>(frame.eax, null), new List<Temp>(lado_dir, new List<Temp>(frame.eax, null))));
			emit(new OPER("MOV `d0, `s0 ;munchBinOp (DIV 3)", new List<Temp>(temp_ret, null), new List<Temp>(frame.eax, null)));
			break;        	
        
    	case BINOP.AND:
    		
    		/* atualiza o valor da esquerda e faz o "e logico" */ 
			emit(new OPER("MOV `d0, `s0 ;munchBinOp (AND)", new List<Temp>(temp_ret, null), new List<Temp>(lado_esq, null)));
			emit(new OPER("AND `d0, `s0 ;munchBinOp (AND)", new List<Temp>(temp_ret, null), new List<Temp>(lado_dir, new List<Temp>(temp_ret, null))));
			break;  
        	
    	case BINOP.OR:
    		
    		/* atualiza o valor da esquerda e faz o "ou logico" */ 
			emit(new OPER("MOV `d0, `s0 ;munchBinOp (OR)", new List<Temp>(temp_ret, null), new List<Temp>(lado_esq, null)));
			emit(new OPER("OR `d0, `s0 ;munchBinOp (OR)", new List<Temp>(temp_ret, null), new List<Temp>(lado_dir, new List<Temp>(temp_ret, null))));
			break;  
    	
    	case BINOP.XOR:
            
    		/* atualiza o valor da esquerda e faz o "ou exclusivo" */ 
			emit(new OPER("MOV `d0, `s0 ;munchBinOp (XOR)", new List<Temp>(temp_ret, null), new List<Temp>(lado_esq, null)));
			emit(new OPER("XOR `d0, `s0 ;munchBinOp (XOR)", new List<Temp>(temp_ret, null), new List<Temp>(lado_dir, new List<Temp>(temp_ret, null))));
			break;  
			
        case BINOP.LSHIFT:
        	
    		/* atualiza o valor da esquerda e faz o "shift para esquerda" */ 
			/* segundo membro deve ser uma constante */
        	emit(new OPER("MOV `d0, `s0 ;munchBinOp (LSHIFT)", new List<Temp>(temp_ret, null), new List<Temp>(lado_esq, null)));
			emit(new OPER("SHL `d0, " + ((CONST)s.right).value + " ;munchBinOp (LSHIFT)", new List<Temp>(temp_ret, null), new List<Temp>(temp_ret, null)));
			break;  
        	
        case BINOP.RSHIFT:
        	
        	/* atualiza o valor da esquerda e faz o "shift para direita" */ 
			/* segundo membro deve ser uma constante */
        	emit(new OPER("MOV `d0, `s0 ;munchBinOp (RSHIFT)", new List<Temp>(temp_ret, null), new List<Temp>(lado_esq, null)));
			emit(new OPER("SHR `d0, " + ((CONST)s.right).value + " ;munchBinOp (RSHIFT)", new List<Temp>(temp_ret, null), new List<Temp>(temp_ret, null)));
			break;  
        	
        case BINOP.ARSHIFT:
        	
        	/* atualiza o valor da esquerda e faz o "shift para direita" */ 
			/* segundo membro deve ser uma constante */
        	emit(new OPER("MOV `d0, `s0 ;munchBinOp (ARSHIFT)", new List<Temp>(temp_ret, null), new List<Temp>(lado_esq, null)));
			emit(new OPER("SAR `d0, " + ((CONST)s.right).value + " ;munchBinOp (ARSHIFT)", new List<Temp>(temp_ret, null), new List<Temp>(temp_ret, null)));
			break;  
      
    	}
  
        return temp_ret;
	}

    /* funcao auxiliar para o munchCall */
    private void empilhaArgumentos(List<Exp> args)
    {
    	List<Temp> lista_temp = null;

		/* cria lista de temporarios na ordem inversa */
		while(args != null)
		{
			Temp temp = munchExp(args.head);
			lista_temp = new List<Temp>( temp, lista_temp);
			args = args.tail;
		}
    	
		/* emite as instrucoes de push na pilha */
		while (lista_temp != null)
		{
			Temp temp = lista_temp.head;			
			emit(new OPER("PUSH `s0 ;empilhaArgumentos", new List<Temp>(frame.esp, null), new List<Temp>(temp, new List<Temp>(frame.esp, new List<Temp>(frame.ebp, null)))));
			lista_temp = lista_temp.tail;
		}
	}
    
    /**
     * Trata uma CALL
     * Faz a chamada de uma procedure
     * empilha os argumentos e faz a chamada
     * salvando o que for necessario depois
     */
	private Temp munchCall(CALL s) 
	{
		Temp temp_ret = new Temp();
		
		/* empilhas os argumentos */
		empilhaArgumentos(s.args);
		
		/* constroi a chamada */
		if (s.func instanceof NAME) {
			emit(new OPER("CALL " + ((NAME) s.func).label.toString() + "; munchCall Name",
			              frame.calldefs, 
			              new List<Temp>(frame.esp, new List<Temp>(frame.ebp, null))));
			
			
		}
		else 
		{
			Temp temp_call = munchExp(s.func);
			emit(new OPER("CALL `s0; munchCall Temp", 
			              frame.calldefs,
			              new List<Temp>(temp_call, new List<Temp>(frame.esp, new List<Temp>(frame.ebp, null)))));
		}
		
		/* verifica se deve restaurar a pilha */
		if (s.args.size() > 0)
		{
			emit(new assem.OPER("ADD `d0," + (4*s.args.size()) + " ; munchCall restaura pilha", 
			                    new List<Temp>(frame.esp, null),
			                    new List<Temp>(Frame.esp, null)));
			
		}
		
		/* A resposta da funcao chamada estara sempre em eax */
		emit(new OPER("MOV `d0, `s0 ;munchCall", 
		              new List<Temp>(temp_ret, null),
		              new List<Temp>(Frame.eax, null)));
		
		return temp_ret;
	}
    
    /**
     * Trata uma constante
     * Carrega a constante 
     * em um temp
     */
	private Temp munchConst(CONST s)
	{
		Temp temp_ret = new Temp();
		emit(new OPER("MOV `d0, " + s.value + " ; munchConst", new List<Temp>(temp_ret, null), null));		
		return temp_ret;
	}
    
	/** 
	 * Trata o caso de um jump condicional
	 * Procura o tipo do jump e 
	 * emite instrucao
	 */
    private void munchCJump(CJUMP s)
    {
    	/**
    	 * CJUMP necessita de uma comparacao antes
    	 */
    	Temp temp_esq = munchExp(s.left);
    	Temp temp_dir = munchExp(s.right);
    	
		emit(new OPER("CMP `s0, `s1; munchCJump", null, new List<Temp>(temp_esq, new List<Temp> (temp_dir, null))));
		
		/**
		 * Para jumps "longe" necessita um label auxiliar
		 */
		Label label_aux = new Label();
		
		/**
		 * Verifica o tipo de jump para criar o codigo
		 */
		switch(s.op)
		{
		case CJUMP.EQ:
			emit(new OPER("JE `j0 ; munchCJump", null, null, new List<Label> (label_aux, new List<Label> (s.ifTrue, new List<Label> (s.ifFalse, null)))));
			break;
		case CJUMP.NE:
			emit(new OPER("JNE `j0; munchCJump", null, null, new List<Label> (label_aux, new List<Label> (s.ifTrue, new List<Label> (s.ifFalse, null)))));
			break;
		case CJUMP.LT:
			emit(new OPER("JL `j0 ; munchCJump", null, null, new List<Label> (label_aux, new List<Label> (s.ifTrue, new List<Label> (s.ifFalse, null)))));
			break;
		case CJUMP.LE:
			emit(new OPER("JLE `j0; munchCJump", null, null, new List<Label> (label_aux, new List<Label> (s.ifTrue, new List<Label> (s.ifFalse, null)))));
			break;
		case CJUMP.GT:
			emit(new OPER("JG `j0 ; munchCJump", null, null, new List<Label> (label_aux, new List<Label> (s.ifTrue, new List<Label> (s.ifFalse, null)))));
			break;
		case CJUMP.GE:
			emit(new OPER("JGE `j0; munchCJump", null, null, new List<Label> (label_aux, new List<Label> (s.ifTrue, new List<Label> (s.ifFalse, null)))));
			break;
		case CJUMP.ULT:
			emit(new OPER("JB `j0 ; munchCJump", null, null, new List<Label> (label_aux, new List<Label> (s.ifTrue, new List<Label> (s.ifFalse, null)))));
			break;
		case CJUMP.ULE:
			emit(new OPER("JBE `j0; munchCJump", null, null, new List<Label> (label_aux, new List<Label> (s.ifTrue, new List<Label> (s.ifFalse, null)))));
			break;
		case CJUMP.UGT:
			emit(new OPER("JA `j0 ; munchCJump", null, null, new List<Label> (label_aux, new List<Label> (s.ifTrue, new List<Label> (s.ifFalse, null)))));
			break;
		case CJUMP.UGE:
			emit(new OPER("JAE `j0; munchCJump", null, null, new List<Label> (label_aux, new List<Label> (s.ifTrue, new List<Label> (s.ifFalse, null)))));
			break;
		}
		
		/* faz o jump para false */
		emit(new OPER("JMP `j0 ; munchCJump", null, null, new List<Label> (s.ifFalse, null)));

		/* faz o jump para false */
		emit(new assem.LABEL(label_aux.toString() + ":", label_aux));
		emit(new OPER("JMP `j0 ; munchCJump", null, null, new List<Label> (s.ifTrue, null)));
	}

    /**
     * Trata o ESEQ
     * Primeiro trata o Stm
     * Depois retorna a exp
     */
	private Temp munchESEQ(ESEQ s) 
	{
		munchStm(s.stm);
		return munchExp(s.exp);
	}
    
    /**
     * Trata o caso de uma Exp
     * Chama o metodo que trata
     * a expressao contida no EXPSTM
     * OBS: ignora o retorno da expressao
     */
    private void munchExpStm(EXPSTM s) 
	{
		munchExp(s.exp);
	}

	/**
	 * Trata uma expressao
	 * Verifica qual o tipo possivel desta
	 * expressao e chama o tratador correto
	 */
    private Temp munchExp(Exp s)
	{
    	/**
		 * guarda resultado da expressao
		 */
		Temp temp = new Temp();
		
		/**
		 * Verifica de que tipo eh a expressao, pode ser:
		 * BINOP, CALL, CONST,  ESEQ, MEM, NAME, TEMP
		 */
		if (s instanceof BINOP)
		{
			temp = munchBinOp((BINOP) s);
		}
		else if (s instanceof CALL)
		{
			temp = munchCall((CALL) s);
		}
		else if (s instanceof CONST)
		{
			temp = munchConst((CONST) s);
		}
		else if (s instanceof ESEQ)
		{
			temp = munchESEQ((ESEQ) s);
		}
		else if (s instanceof MEM)
		{
			temp = munchMem((MEM) s);
		}
		else if (s instanceof NAME)
		{
			temp = munchName((NAME) s);
		}
		else if (s instanceof TEMP)
		{
			temp = munchTemp((TEMP) s);
		}
		else
		{
			System.out.println("nao bateu em nada");
		}
		return temp;
	}

	/**
     * Trata um Jump
     * Verifica se salta para um label
     * ou endereco calculado e emite
     * a instrucao
     */
	private void munchJump(JUMP s)
	{
		if (s.exp instanceof NAME)
		{
			
			/* salto para label */
			NAME nome = (NAME) s.exp;
			Label label = nome.label;
			emit(new OPER("JMP `j0 ; munchJump", 
			              null,
			              null,
			              new List<Label>(label, s.targets)));
		}
		else
		{
			/* salto para endereco */
			Temp end = munchExp(s.exp);
			emit(new OPER("JMP `s0; munchJump",
					      null,
					      new List<Temp>(end, null),
					      s.targets));
		}
	}
    
	/**
	 * Trata um Label
	 * Imprime o label
	 */
	private void munchLabel(LABEL s) 
	{
		emit(new assem.LABEL(s.label.toString()+":", s.label));
	}
	

	/**
	 * Trata um acesso a memoria
	 *  Verifica o tipo do indice para ver como 
	 *  emitir a instrucao
	 */
	private Temp munchMem(MEM s) 
	{
		Temp temp_ret = new Temp();
				
		if (s.exp instanceof BINOP)
		{
			/* expressao eh um BINOP */
			/* MEM(X+-Y)              */
			BINOP exp = (BINOP) s.exp;
			String sinal = "";
			boolean emite_inst = true;
			
			/* verifica o sinal correto */
			if (BINOP.PLUS == exp.binop)
			{
				sinal = "+";
			}
			else if (BINOP.MINUS == exp.binop)
			{
				sinal = "-";
			}
			else
			{
				emite_inst = false;
			}
			
			/* emite a instrucao */ 
			if (true == emite_inst)
			{
				/* verifica o tipo dos operadores*/
				if (exp.left instanceof CONST && exp.right instanceof CONST)
				{
					//CONST e CONST
					emit(new OPER("MOV `d0, [" + ((CONST)exp.left).value + sinal + ((CONST)exp.right).value +"] ;munchMem(C" + sinal + "C)", new List<Temp>(temp_ret, null), null));
					return temp_ret;
				}
				else if (exp.left instanceof CONST)
				{
					//CONST e TEMP
					Temp lado_dir = munchExp(exp.right);
					emit(new OPER("MOV `d0, [" + ((CONST)exp.left).value + sinal + "`s0] ;munchMem(C" + sinal + "T)", new List<Temp>(temp_ret, null), new List<Temp>(lado_dir, null)));
					return temp_ret;
				}
				else if (exp.right instanceof CONST)
				{
					//TEMP e CONST
					Temp lado_esq = munchExp(exp.left);
					emit(new OPER("MOV `d0, [`s0" + sinal + ((CONST)exp.right).value + "] ;munchMem(T" + sinal + "C)", new List<Temp>(temp_ret, null), new List<Temp>(lado_esq, null)));
					return temp_ret;
				}	
			}
		}

		if (s.exp instanceof CONST)
		{
			/* expressao eh uma CONST */
			/* MEM(CONST) */
			emit(new OPER("MOV `d0, [" + ((CONST) s.exp).value + "] ;munchMem(C)", new List<Temp>(temp_ret,null), null));
			return temp_ret;
		}
		
		/*  recupera de um temp  */
		/*  MEM(TEMP)            */
		Temp temp = munchExp(s.exp);
		emit(new OPER("MOV `d0,[`s0] ; munchMem(T)", new List<Temp>(temp_ret, null), new List<Temp> (temp, null)));
				
		return temp_ret;
	}
	
    
	/**
	 * Trata um caso de Move dst, src
	 * MOVE pode ser
	 *   mov mem, reg
	 *   mov mem, imediato
	 *   mov reg, reg 
	 *   mov reg, mem
	 *   mov reg, imediato
	 */
    private void munchMove(MOVE s)
    {
    	
    	/* Verifica se o destino eh memoria ou expressao (const, temp, etc) */
    	if (s.dst instanceof MEM)
    	{
    		/* EH MEM */
    		MEM dest_mem = (MEM) s.dst;
    		
    		/* recupera a expressao source e joga num registrador */
    		Temp temp_src = munchExp(s.src);
    		    		  		
    		/* verifica o tipo da expressao */
    		if (dest_mem.exp instanceof CONST)
    		{
    			/* expressao eh uma CONST */
    			/* MOVE [CONST], EXP */
    			emit(new OPER("MOV [" + ((CONST) dest_mem.exp).value + "], `s0 ;munchMove( MEM[C], EXP)",
    					      null,
    					      new List<Temp>(temp_src,null)));
    			return;
    		}
    		if ( dest_mem.exp instanceof BINOP)
    		{
    			BINOP dest_ind = (BINOP) dest_mem.exp;
    			
    			/* pode ser uma soma ou subtracao */
    			String sinal = null;
    			boolean emite_inst = true;
    			if (BINOP.PLUS == dest_ind.binop)
    			{
    				sinal = "+";
    			}
    			else if (BINOP.MINUS == dest_ind.binop)
    			{
    				sinal = "-";
    			}
    			else
    			{
    				emite_inst = false;
    			}
    			
    			/* eh uma soma ou subtracao */ 
    			if (emite_inst == true)
    			{
    				/* verifica o tipo dos operadores*/
    				if (dest_ind.left instanceof CONST && dest_ind.right instanceof CONST)
    				{
    					//MOVE [CONST op CONST], EXP
    					emit(new OPER("MOV [" + ((CONST)dest_ind.left).value + sinal + ((CONST)dest_ind.right).value +"], `s0 ;munchMove(MEM[C" + sinal + "C],EXP)", 
    						          null,
    						          new List<Temp>(temp_src, null)));
    					return;
    				}
    				if (dest_ind.left instanceof CONST)
    				{
    					// MOVE [CONST op TEMP], EXP
    					Temp lado_dir = munchExp(dest_ind.right);
    					emit(new OPER("MOV [" + ((CONST)dest_ind.left).value + sinal + "`s0], `s1  ;munchMove(MEM[C" + sinal + "T],EXP)",
    							      null, 
    							      new List<Temp>(lado_dir, new List<Temp>(temp_src, null))));
    					return;
    				}
    				if (dest_ind.right instanceof CONST)
    				{
    					// MOVE [TEMP op CONST], EXP
    					Temp lado_esq = munchExp(dest_ind.left);
    					emit(new OPER("MOV [`s0" + sinal + ((CONST)dest_ind.right).value + "], `s1 ;munchMove(MEM[T" + sinal + "C], EXP)", 
    						          null,
    						          new List<Temp>(lado_esq, new List<Temp>(temp_src, null))));
    					return;
    				}    		
    			}   			
    		}
    		
    		// MOVE [EXP], EXP
    		Temp dst_exp = munchExp(dest_mem.exp);
    		emit(new OPER("MOV [`s0], `s1 ;munchMove(MEM[T], EXP)", 
			              null,
				          new List<Temp>(dst_exp, new List<Temp>(temp_src, null))));
    		return;    		 		
    	}
    	else
    	{
    		/* destino = Exp */
    		Temp temp_dst = munchExp(s.dst);
    		
    		/* verifica o tipo da expressao SRC */
    		if (s.src instanceof CONST)
    		{
    			/* expressao eh uma CONST */
    			/* MOVE EXP, [CONST] */
    			emit(new OPER("MOV `d0, " + ((CONST) s.src).value + " ;munchMove( EXP, MEM[C])",
    					      new List<Temp>(temp_dst,null),
    					      null));
    			return;
    		}
    		if ( s.src instanceof MEM)
    		{
    			/* EH MEM */
    	    	MEM src_mem = (MEM) s.src;
    	    		
    	    	/* verifica o tipo da expressao */
    	    	if (src_mem.exp instanceof CONST)
	    		{
	    			/* expressao eh uma CONST */
	    			/* MOVE EXP, [CONST] */
        			emit(new OPER("MOV `d0, [" + ((CONST) src_mem.exp).value + "] ;munchMove(EXP, MEM[C])",
	    					      new List<Temp>(temp_dst,null),
	    					      null));
        			return;
	    		}
    	    	if ( src_mem.exp instanceof BINOP)
	    		{
    	    		BINOP src_ind = (BINOP) src_mem.exp;
    	    			
    	    		/* pode ser uma soma ou subtracao */
    	    		String sinal = null;
    	    		boolean emite_inst = true;
    	    		if (BINOP.PLUS == src_ind.binop)
    	    		{
    	    			sinal = "+";
    	    		}
    	    		else if (BINOP.MINUS == src_ind.binop)
    	    		{
    	    			sinal = "-";
    	    		}
    	    		else
    	    		{
    	    			emite_inst = false;
    	    		}
    	    			
    	    		/* eh uma soma ou subtracao */ 
    	   			if (emite_inst == true)
    	   			{
    	   				/* verifica o tipo dos operadores*/
    	   				if (src_ind.left instanceof CONST && src_ind.right instanceof CONST)
    	   				{
    	    				//MOVE EXP, [CONST op CONST]
    	   					emit(new OPER("MOV `d0, [" + ((CONST)src_ind.left).value + sinal + ((CONST)src_ind.right).value +"] ;munchMove(EXP, MEM[C" + sinal + "C])",
    	    							  new List<Temp>(temp_dst, null),
    	    							  null));
    	   					return;
    	    			}
    	    			if (src_ind.left instanceof CONST)
    	    			{
    	    				// MOVE EXP,[CONST op TEMP]
    	    				Temp lado_dir = munchExp(src_ind.right);
    	        			emit(new OPER("MOV `d0, [" + ((CONST)src_ind.left).value + sinal + "`s0] ;munchMove(EXP, MEM[C" + sinal + "T])",
    	    							  new List<Temp>(temp_dst, null),
    	    							  new List<Temp>(lado_dir, null)));
    	        			return;
    	    			}
    	    			if (src_ind.right instanceof CONST)
    	    			{
    	    				// MOVE EXP, [TEMP op CONST]
    	    				Temp lado_esq = munchExp(src_ind.left);
    	        			emit(new OPER("MOV `d0, [`s0" + sinal + ((CONST)src_ind.right).value + "] ;munchMove(EXP, MEM[T" + sinal + "C])", 
    	    						      new List<Temp>(temp_dst, null),
    	    						      new List<Temp>(lado_esq, null)));
    	        			return;
	    				}    		
	    			}/* emite_instr*/   			
    	    	}/* binop */ 

    	    	// MOVE EXP, [EXP]
    			Temp src_exp = munchExp(src_mem.exp);
	    		emit(new OPER("MOV `d0, [`s0] ;munchMove(EXP, MEM[T])", 
    						      new List<Temp>(temp_dst, null),
    						      new List<Temp>(src_exp, null)));
	    		return;	    		   		
    		} /* MEM */ 

    		// MOVE EXP, EXP
			Temp temp_src = munchExp(s.src);
    		emit(new OPER("MOV `d0, `s0 ;munchMove(EXP, EXP)", 
					          new List<Temp>(temp_dst, null),
					          new List<Temp>(temp_src, null)));
    		return;    		
    	}    	
	}
	
	/**
	 * Trata um Name
	 * move para um temp
	 */
	private Temp munchName(NAME s) 
	{
		Temp temp_ret = new Temp();
		emit( new OPER("MOV `d0, " + s.label.toString() + " ; munchName", new List<Temp>(temp_ret,null), null));
		return temp_ret;
	}
	
	/**
	 * Trata um Seq
	 * Chama o tratamento para cada
	 * statement formador desta SEQ
	 */
	private void munchSeq(SEQ s) 
	{
		munchStm(s.left);
		munchStm(s.right);
	}
	
	/**
	 * Trata um Temp
	 * somente retorna uma referencia 
	 */
	private Temp munchTemp(TEMP s) 
	{
		return s.temp;
	}

	
	/*-------------------------------------------------------------*
     *                              MAIN                           *
     *-------------------------------------------------------------*/
    List<Instr> codegen(Stm s)
    {
        List<Instr> l;
        munchStm(s);
        l=ilist;
        ilist=last=null;
        return l;
    }
    
    List<Instr> codegen(List<Stm> body)
    {
        List<Instr> l = null, t = null;
        
        for( ; body != null; body = body.tail )
        {
            munchStm(body.head);
            if ( l == null )
                l = ilist;
            else
                t.tail = ilist;
            t = last;
            ilist=last=null;
        }	
        return l;
    }
}
